《Cocoa Design Patterns》阅读笔记

最近事情太多了,偷懒把15年的读书笔记发上来充个数。

Question

category用的是哪种设计模式?

不属于23种设计模式的任何一种,本身就是一种设计模式

perform selector用的是哪种设计模式?

类似于命令模式,NSInvocation是一种更为完备的命令模式

bundle用的是哪种设计模式?

bundle本身就是一种设计模式,类似facade

binding是什么?

In the C programming language and languages derived from it like Objective-C and C++, compilers and linkers convert explicit function calls in program source code into machine language jumps to code at predetermined fixed addresses in memory.The conversion of code to fixed addresses in sometimes called binding

mac开发中还有一种binding是利用KVO做双向绑定,具体实现原理不清楚,但有类似的ios开源库(Bind),可以研究下

访问者和命令模式在cocoa的哪些地方有用到?

NSProxy

装饰者模式在cocoa的哪些地方有用到?

UIView的子类、category

摘要笔记

One Pattern to Rule Them All

Model View Controller

Fundamental Patterns

two stage creation

分离成两个步骤后,有desinated initializer和非desinated initializer,方便创建非desinated initializer

template method

If you find yourself contemplating the creation of Template Methods in code intended for reuse, first consider Delegates. In many cases, Delegates provide a more flexible alter- native to Template Methods.

dynamic creation

One very important aspect of this power is the fact that this function allows a developer to use an object that was unknown to the compiler and linker when the application was built

Heterogeneous Containers

The NSArray and NSMutableArray classes store ordered collections of object refer- ences.The NSDictionary and NSMutableDictionary classes store associations of keys and values for rapid retrieval.The NSSet and NSMutableSet classes store unordered col- lections of unique object references; each object can be referenced at most once in any particular set. Finally, NSCountedSet, a subclass of NSMutableSet, provides an unordered collection, but it allows multiple references to the same object within one collection.

NSOrderedSet NSMutableOrderedSet

Enumerators

一个通过继承(NSEnumerator),一个通过协议(NSFastEnumeration)

perform selector

The Invocation pattern and the NSInvocation class provide a more complete implementation of late binding of messages than the simple –performSelector: method. NSInvocation can store messages that have nonobject return types or require complex arguments.

An Action is really just a variable selector, and the target is just an anonymous object

accessor

Methods like NSData’s -(void)getBytes:(void )aBuffer return values by reference because the number of bytes that will be returned can’t be determined at compile time. Other examples of get methods include NSArray’s -(void)getOb- jects:(id )aBuffer, NSString’s -(void)getCharacters:(unichar )aBuffer, and NSValue’s -(void)getValue:(void )aBuffer. In each case, the size of the value copied into the referenced memory is variable.Another reason for returning values by ref- erence is to enable the return of more than one value from a single method.A good ex- ample of that is NSColor’s -(void)getRed:(CGFloat )red green:(CGFloat )green blue:(CGFloat )blue alpha:(CGFloat )alpha method, which returns four floating point values by reference.

  • (NSString *)title {
    return _myTitle;
    }
    The -title implementation is adequate in most cases, but a more sophisticated tech- nique may be necessary if the application using the title is multithreaded. In a multithreaded
    application, there is a chance that another thread of execution may alter _myTitle or release it after it has been returned from the -title method but before the code that called -title has had an opportunity to retain the returned object. In that case, _myTitle’s retain count may reach zero, and the object may be deallocated, leaving the code that called -title with a pointer to a deallocated (invalid) object. One solution that supports the multithreaded case is to retain and autorelease the ob- ject being returned as follows:
  • (NSString *)title {
    id result;
    // Lock
    result = [[_myTitle retain]() autorelease];
    // Unlock
    return result];
    }

archiving and unarchiving

Conditional Encoding

Patterns That Primarily Empower by Decoupling

singleton

Classes exist to encapsulate information about instances and provide a single inter- face for creating instances. Using an instance of MYGameHighScoreManager to manage scores and using the MYGameHighScoreManager class itself to encapsulate access to the in- stance is the cleanest way to achieve the goals of flexibility and maintainability.

nsnotification

The object that sends a message to a notification center doesn’t need to know what observers exist or how many observers ultimately receive the notification. Similarly, the observers don’t necessarily need to know where notifications originate

Use the Notification pattern when anonymous objects need to passively observe and react to important events. In contrast, use the Delegates pattern when anonymous objects need to actively influence events as they happen.

delegate

Delegates simplify the customization of object behavior while minimizing coupling be- tween objects

delegate vs subclassing

Subclassing statically establishes the relationship between the subclass and its superclass at compile time. In many cases, runtime flexibility is desired. For example, constraints on the resizing behav- ior of a window might change based on user actions at runtime

datasource

Data sources are similar to delegates, but they play a different role. Delegates react to changes or control other objects.A data source provides data to another object whenever needed. Delegates are always optional; the object that uses a delegate falls back to default behavior if there is no delegate assigned.An object that uses a data source may not be functional without a valid data source to supply data. For example, Cocoa’s NSTableView class retrieves data as needed from a data source. Using a data source provides several advantages. First and most importantly, using a data source preserves the separation of subsystems in the Model-View-Controller pattern.The graphical table drawing and editing features of NSTableView clearly belong in the View subsystem. Calculation, retrieval, and storage of the data values to be displayed are clearly part of the Model subsystem.The calculations and storage of data remains the same even if the mechanism used to display the data changes.The same Model data could be displayed in a pie chart or output to a file or sent to a printer.The object acting as a data source for an instance of NSTableView is typically part of the Controller subsystem.The data source responds to the NSTableView’s requests for data by retrieving the data from the Model. NSTableView is decoupled from details regarding the retrieval of data from the Model. Similarly, classes in the Model subsystem have no coupling to the View objects that dis- play data. Using a data source has the added advantage of enabling efficient data processing and memory usage. For example, even if a table has a million rows, at most a few dozen rows can be seen on the screen at once. NSTableView only asks its data source for the data needed to display the currently visible rows. If the Model subsystem needs to calculate the data or the data must be fetched over a network from a database, there is no need to cal- culate or fetch a million rows of data at once. Like a delegate, the data source object is not retained by the object that uses it. A single object acting as a data source may provide data to any number of objects.Apple identifies the messages that will be sent to a data source in the class documentation for the classes that require a data source. Just like delegate methods, there is typically an informal proto- col that declares the methods a data source must implement.

outlets target action

Any instance variable with type id and a name that doesn’t start with an underscore character is automatically considered an outlet. In addi- tion, any instance variable that is a pointer to an object and includes the IBOutlet macro in its declaration is treated as an outlet.

The IBOutlet macro is defined in NSNibDeclarations.h, which is part of Cocoa’s Application Kit framework, and the C preprocessor replaces it with a single space charac- ter whenever it’s encountered in source code. IBOutlet doesn’t change the meaning of compiled code at all. It is just a hint to Interface Builder that identifies outlets with types more specific than id.When a specific type is declared, Interface Builder will respect it and use that information to limit what types of objects may be connected to that outlet.

The IBAction type is actually a preprocessor macro that evaluates to the void type.

MVC之间的耦合

With regard to coupling, the MYPlayerController class knows about the MYSongPlayer object in the Model and is therefore coupled to the Model, but the Model knows nothing about the Controller subsystem.The MYPlayerController instance also has outlets that are con- nected toView objects.The Controller is slightly coupled to theView but knows very lit- tle about theView objects with which it communicates.TheView has no dependence on the Controller.The buttons and slider have targets defined with type id, which means they could be connected to any objects that respond to the assigned action messages.

MVC信息流

responder chain

As a user works with an application, Cocoa automatically tracks where the user’s focus is.The window currently receiving keyboard input is known as the “key” window.The currently focused document is known as the “main” window. Usually a user is working directly in the document, so the key and main windows are the same. Sometimes the user’s focus is actually on two windows, however. For example, in a multidocument appli- cation with utility panels, the user might be focused on a particular document while entering something into a utility panel.The input to the utility panel is expected to mod- ify the focused document in some way. In this case, the utility panel would be the key window, while the document it affects is the main window

Incoming keyboard and mouse events are converted to NSEvent objects by the NSApplication object and then dispatched to the correct responder object by passing them down a Responder Chain. For a keyboard events and mouse moved events, the first responder of the key window is the head of the chain. For mouse clicks, the most deeply nested view directly under the mouse is the head. If a responder doesn’t want to handle the event, then it will pass the event on to the next responder in the chain until either an object handles the event or the event reaches the end of the chain.

  1. Start with the first responder of the key window.
  2. Follow the Responder Chain up the view hierarchy.
  3. Try the window object.
  4. Try the window’s delegate,which is often an NSDocument instance.
  5. Next is an NSWindowController instance,if there is one.
  6. Repeat 1-5 starting with the first responder of the main window.
  7. Try the NSApplication object and its delegate.
  8. Try the NSDocumentController,ifthereisone.

Associative Storage

In addition to conforming to the NSCopying protocol, objects used as keys in a dictionary must implement the -isEqual: and -hash methods so that any two objects that are considered equal by the -isEqual: method also have the same hash value.The -isEqual: and -hash methods are declared in the NSObject class, which provides basic implementations using the addresses of objects. In other words, two objects are equal if they have the same address, and the -hash value is computed from the address.

The key value system allows pro- grams to interact with objects as if every object is a dictionary that associates its properties with string keys

KeyValue Coding provides a way for scripting languages to access the values stored in objects at runtime based on the string name of the value. Perhaps more importantly, Key Value Coding is the foundation for Cocoa bindings technology. Key Value Coding applies the Associative Storage pattern to every Cocoa object so that object instance variables or accessors are selected at runtime via string keys.

KVC/KVO is a variation of the Associative Storage pattern, which lets you access the properties of ob- jects as if every object were a simple dictionary of key/value pairs

invocation

One point of confusion for many developers is in how messages are named. Each name has two parts, the selector and the method signature. Both parts are required to properly configure NSInvocation objects. A selector is the message’s name without any type infor- mation, for example “countOfObjectsOfType:” is a selector. Objective-C also has the data type SEL, which is a unique identifier representing a selector.The Objective-C direc- tive @selector() can be used to obtain a SEL.

Most programmers consider the selector to be the same thing as the message name, and in most cases this is fine. However, selectors do not provide any type information. To build a complete message, the types of each argument and the return value’s type need to be known.This type information is known as a method signature.The class NSMethodSignature encapsulates this information

Invocations package up an Objective-C method so that it can be handled as if it were an object

  • (id)tellPerson:(id)person
    {

}

selector(用sel表示)
tellPerson: @selector(tellPerson:)

method signature (用NSMethodSignature表示)
id id

invocation创建及执行流程

NSString *receivingString = [receiver stringValue];  
NSString *messageString = [message titleOfSelectedItem];  
SEL selector = NSSelectorFromString(messageString);  
NSMethodSignature *methodSignature = [receivingString methodSignatureForSelector:selector];  
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 

[invocation setTarget:receivingString]; // argument 0 is "self"  
[invocation setSelector:selector]; // argument 1 is "_cmd" 

int numberOfArguments = [[message selectedItem] tag];  
if (numberOfArguments > 0) 
{ 
NSString *argumentString1 = [argument1 stringValue];  
[invocation setArgument:&argumentString1 atIndex:2];  
if (numberOfArguments > 1) 
{ 
NSString *argumentString2 = [argument2 stringValue]; 
[invocation setArgument:&argumentString2 atIndex:3];  
} 
} 


[invocation invoke]; 
void *returnValue = NULL; 
[invocation getReturnValue:&returnValue]; 
const char *returnType = [methodSignature methodReturnType]; 
if (returnType) { 
switch (returnType[0]) { 
case ‘@': 
[result setObjectValue:(id)returnValue]; break; 
case ‘i': 
[result setIntValue:(int)returnValue]; break; 
default: break; 
}  
} 

prototype

The most essential feature of prototype objects is that they can be copied

flyweight

There may be a lot of NSNumber instances in use at any one time in a Cocoa program. Cocoa optimizes storage for NSNumber instances by sharing them. Each time you call NSNumber numberWithInt:0;, you are likely to get the same instance returned. Cocoa keeps a cache of recently or frequently used NSNumber instances.When you ask for a new NSNumber that stores the same value as a cached instance, the cached instance is returned instead of a new instance

Patterns That Primarily Hide Complexity

The patterns in Part IV hide complexity and implementation details so programmers can focus on solving problems.
编程是为了解决问题,不要被技术细节困住,专注于解决问题

bundle

iOS

Mac OS

class cluster

多例模式:可以用这个模式设计内存池

facade

The philosophy is to keep common interaction simple while making complex interaction possible

proxy and forwarding

The Proxy pattern allows messages to be sent to an object that is separated from the mes- sage’s sender by time or space. Proxies can also control access to or alter the behavior of other objects. Forwarding simplifies the capture of messages as invocations so that they can be resent, delayed, repeated, stored, or altered.

proxy

A proxy can simulate a category with instance variables by defining its own variables and methods and then forwarding everything else to the real object.

Furthermore, when a category overrides an object’s existing methods, it has no easy way to access the original method, but a proxy can.
给个例子啊。。。

Proxies are also able to simulate multiple inheritance by for- warding messages to multiple other objects, creating a form of composite object.

manager

The Manager pattern is a hybrid and a generalization of other patterns such as Singleton, Façade, and Controllers.

In custom code, a manager should be used when a class is needed for managing instances of other classes, especially when uniqueness is required.

Core Data

Application Kit Views

When input data becomes available, the run loop translates the data into events and sends Objective-C messages to various objects to process the events.

The key window receives key- board events.The main window is the window that is affected by actions in the key win- dow.The key window and the main window are usually the same, but in some cases they might be different

The first view to receive an event-processing message depends on the type of the event.The first mouse-down event within a window that is not the key window is usually consumed by the window itself to make the window into the key window and bring it to the front.This behavior can be modified in several ways. For example, a subclass of NSView can override the - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent method to return YES based on the mouse event. Returning YES means that the view is able to use the first mouse click in an inactive window. NSWindow sends mouse-down and mouse-move event messages to the top-most view under the mouse. Subviews are drawn after their superview.The top-most view under the mouse is therefore usually the most deeply nested view under the mouse. Mouse-move events occur frequently and are seldom used. NSWindow does not send mouse move event messages to views by default. If a subclass of NSView needs to receive mouse-move events, it must tell NSWindow to send them. NSWindow’s -(void)setAcceptsMouseMovedEvents: (BOOL)acceptMouseMovedEvents method is used to tell the window to send mouse- move event messages to views. Mouse-drag and mouse-up event messages are sent to the view that received the corresponding mouse-down event. Keyboard event messages are sent to the first responder within the window.The NSView class implements the - (BOOL)acceptsFirstResponder method to always return NO. As a result, most views never become the first responder within a window.

Objective-C mes- sages that have one object argument are called actions

The role of the shared NSApplication object in the target-action implementation is important. If the target of a user interface element is specified, the shared application ob- ject just sends the action message to the target directly. However, if no target is specified (the to: argument is nil), -sendAction:to:from: uses the Responder Chain to select the object that receives the action message. Setting the target of a user interface element to nil makes the target context-sensitive. If the to: argument to -sendAction:to:from: is nil, NSApplication searches the Responder Chain for an object that can respond to the action message.The search begins with the first responder in the key window. If the first responder can’t respond to the ac- tion message, the next responder is checked and so on until the key window itself is reached. After the key window gets a chance, the key window’s delegate is checked. If the key window’s delegate can’t respond to the action message, and the main window is dif- ferent from the key window, the first responder in the main window is checked.The search for an object that responds to the action continues up the main window’s Respon- der Chain to the main window itself and then the main window’s delegate. If no target has been found, the application object is tried. Finally, if the application object can’t re- spond to the action, the application object’s delegate is given a chance

binding and controller

Inside NSObject’s default implementation of the NSKeyValueObserving informal pro- tocol, -willChangeValueForKey: and -didChangeValueForKey: are implemented to send messages to registered observers before and after the property value changes.

It’s not necessary to explicitly call -willChangeValueForKey: and -didChangeValueForKey: within correctly named Accessor methods.When you use Objective-C 2.0’s @synthesize directive to generate Accessor method implementations, the details are handled for you. Even if you hand-write Accessor methods, Cocoa provides auto- matic support for KeyValue Observing through a little bit of Objective-C runtime manipu- lation briefly described at http://developer.apple.com/documentation/Cocoa/Conceptual/ KeyValueObserving/Concepts/KVOImplementation.html.At runtime,Cocoa is able to replace your implementation of each Accessor method with a version that first calls -willChangeValueForKey:, then calls your implementation, and finally calls -didChangeValueForKey:. When Key Value Coding’s -(void)setValue:(id)value forKey:(NSString )key or -(void)setValue:(id)value forKeyPath:(NSString )keyPath methods are used to modify an observed property, the appropriate Accessor methods (if any) are called, and the Accessor methods take care of calling -willChangeValueForKey: and -did- ChangeValueForKey:. If there aren’t any available Accessor methods, -setValue:forKey: and -setValue:forKeyPath: call -willChangeValueForKey: and -didChangeValueForKey: directly. In summary, you only need to explicitly call -willChangeValueForKey: and -didChangeValueForKey: if you change the value of an observed property without using Key Value Coding and without using an appropriately named Accessor method.

坚持原创技术分享,您的支持将鼓励我继续创作!